VCDEB = 0	; Nonzero for debugging microcode

VCBAD = MAIN

; Versatec now has its own device code 520, with 524 reserved for a second VC.
VCDSP:	
:. + 20		;Reserve space for dispatch table
;Let's save this for a second VC
.REPEAT 10 [	ILGIOT $
		NOP $
];.REPEAT 10

VCORG:		; Origin of Versatec code

; Versatec driver definitions and register usage
;
; CONO - command out.  Takes (E) as
;		Bits 0-15: Command to Versatec
;		Bits 16-29: Unused
;		Bit 30: Enable interrupt on READY
;		Bits 31-32: Mode 00 - unused (same as mode 01)
;				 01 - 7-bit bytes, 5 per word, left justified
;				 10 - 8-bit bytes, 4 per word, left justified
;				 11 - 8-bit bytes, 9 per two words
;		Bits 33-35: PI channel
;
; CONI - status in.  Puts in E
;		Bits 0-17: Zero, except
;			3-4, 10-11: State of corresponding
;					command out bits.
;		Bits 18-23: Status bits from Versatec
;		Bit 24: Reserved
;		Bit 25: Versatec READY bit
;		Bit 26: Data ready bit
;		Bits 27-29: zero
;		Bit 30: ENB-RDY-INT
;		Bits 31-35: Mode and PI from last command out
;
; CONSZ, CONSO - Test right half of above CONI bits.
;
; DATAO - Start output.  Takes (E) as
;		Bits 0-17: 2's complement byte count (0 means 256K bytes)
;		Bits 18-35: Starting memory address
;	This operation sets Busy, clears Done, and starts the output.
;	When output finishes, Busy clears and Done sets.
;
; The following are debugging aids
;
; BLKI - Enters the interrupt routine.
; DATAI - Returns the remaining byte count/MA.
; BLKO - Unused.
;
; A-MEM Usage:	0 - Interrupt Dispatch
;		1 - MA saved between bursts
;		2 - byte count saved between bursts
;		3 - Constants depending on Mode
;			Bit 35: 0 for modes 1&2, 1 for mode 3
;			Bits 32-34: Bytes/Word - 1
;			Bits 25-31: Bytes/burst
;		4 - Command/Status Register
;		5 - Mask for command bits that are levels.
;		6 - Saved AC during output burst
;		7 - Saved PC during output burst
;
; Other registers:
;		PC - Addresses memory during output
;		AC - Counts bytes during output
;		Loop Count - counts bytes in a word
;		Q, AR, HOLD - temporary
;
: VCDSP + 0
.REPEAT 1 - VCDEB [	; Make debugging ops illegal (NOP)
.repeat 3 [	ILGIOT $ NOP $
] ; .REPEAT 3
] ; .REPEAT 1 - VCDEB

.REPEAT VCDEB [		; BLKI - act like the Versatec interrupted

	JUMP[.] NORM $
	D[CONST VCDEV] DEST[DEV-ADR] JUMP[VCINT] NORM $
]

: VCORG + 2	; Dispatch base and interrupt entry

; Dispatch for modes 1-3 relative to the vector (2 words/entry)
;		Set burst count for the mode
	D[CONST 17] ROT[6] DEST[Q] NORM $ ;Mode 1 - 60 bytes/burst, 5 bytes/word
	D[CONST 10] ALU[DORQ] DEST[3] SPEC[DEST-A-MEM] JUMP[VCSET2] NORM $
;
	D[CONST 20] ROT[6] DEST[Q] NORM $ ;Mode 2 - 64 bytes/burst, 4 bytes/word
	D[CONST 06] ALU[DORQ] DEST[3] SPEC[DEST-A-MEM] JUMP[VCSET2] NORM $
;
	D[CONST 17] ROT[6] DEST[Q] NORM $ ;Mode 3 - 63 bytes/burst, 4 bytes/word
	D[CONST 67] ALU[DORQ] DEST[3] SPEC[DEST-A-MEM] JUMP[VCSET2] NORM $
;
VCINT:		;Interrupt routine

: VCORG
.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCINT] NORM $
: VCINT
]
	ALU[0] DEST[IOD] SPEC[IOB-OUT] NORM $ ;Disable interrupts
	MAPF[4] CYLEN[IOB-OUT] SPEC[IOB-OUT]
.REPEAT VCDEB [ $ ] 
.REPEAT 1 - VCDEB [ JUMP[VCINT] $
: VCINT
]
	MAPF[0] SPEC[IOB-OUT] CYLEN[IOB-OUT] $ ; Clear lat-byte bit
	MAPF[5] CYLEN[IOB-OUT] ; Clear request
		D[12] COND[-OBUS=0] JUMP[VCGO] $ ;Output if count/= 0

	D[14] DEST[Q] NORM $ ; Fetch status register
	D[14] ROT[30.] COND[OBUS<0] JUMP[VCINT2] C550 $

; End of data transfer.  Set data ready bit.
	D[CONST 1] ROT[9.] ALU[DORQ] DEST[4]
		SPEC[DEST-A-MEM] JUMP[VCINT3] NORM $

; READY interrupt.  Clear ready int enable bit.
VCINT2:	D[CONST 1] ROT[5] ALU[-D&Q] DEST[4]
		SPEC[DEST-A-MEM] NORM $
 
VCINT3:	D[14] MASK[3] DEST[AR Q CLR-DEV-FROM-INTR]
		COND[-OBUS=0] JUMP[PIGEN] C550 $ ; Macro int if enabled
	JUMP[MAIN] NORM $

;IOT dispatch
	.PAIR
	D[PC] ROT[6 + 1] MASK[1] COND[OBUS=0] JUMP[MUUO] $
		;Trap if User and not IOT-USER
VCIOT:	D[IR] ROT[12. + 1 + 1] MASK[4] DEST[Q] NORM $
		;Extract IOT decode * 2.  Note we can do this because the
		;machine has already done indexing/indirection and bits
		;13:17 are guaranteed zero
	D[10] ROT[18.] MASK[16.] ALU[D+Q] SDISP CYLEN[DISP] $
		;Dispatch of type of IOT

;
; Command out to Versatec

VCCMD:
: VCDSP + 10 ; CONO dispatch
.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCCMD] NORM $
: VCCMD
]
	D[CONST VCDEV] DEST[DEV-ADR] PUSHJ[VCONO] NORM $
	JUMP[MAIN] $
.REPEAT 1 - VCDEB [
: VCCMD
]
; Reset.

VCRST:	D[CONST 6] ROT[30.] DEST[Q] NORM $ ; mask for VC mode bits
	D[CONST 3] ROT[24.] ALU[DORQ]
		DEST[5] SPEC[DEST-A-MEM] NORM $

	D[CONST VCDSP / 100] ROT[6 + 18.] DEST[Q] $	;Setup dispatch
.IF VCDSP \ 100 /= 0 [
	D[CONST VCDSP \ 100] ROT[18.] ALU[DORQ] DEST[Q] $
]
	D[CONST VCORG / 100] ROT[6] ALU[DORQ] DEST[0 Q] ;Interrupt base address
		SPEC[DEST-A-MEM] NORM $
.IF VCORG \ 100 /= 0 [
	D[CONST VCORG \ 100] ALU[DORQ] DEST[0]
		SPEC[DEST-A-MEM] NORM $
]
	D[CONST 10] DEST[HOLD] JUMP[VCONO0] NORM $ ; Invent CONO 124,[10]

; CONO routine

VCONO: FIXM1 $

VCONO0:	SPEC[IOB-OUT]
	D[MEM] ROT[31.] MASK[1] DEST[AR] NORM $ ; Save int-on-rdy bit

	MAPF[0] SPEC[IOB-OUT] CYLEN[IOB-OUT] ; Clear the FIFO
	D[MEM] DEST[IOD Q] $ ;Set up the control bits

	MAPF[1] SPEC[IOB-OUT] LONG ;Output control bits
	D[15] ALU[D&Q] DEST[IOD Q] $ ;Clear the ones that pulse.

	MAPF[5] SPEC[IOB-OUT] LONG ; Clear int req
	D[MEM] MASK[6] ALU[DORQ] DEST[Q] $ ; Save the mode

	MAPF[1] SPEC[IOB-OUT] LONG ; Clear the pulsed bits
	D[AR] ROT[2] DEST[IOD] $ ;Set up the int-on-rdy bit

	MAPF[4] CYLEN[IOB-OUT] ; Set interrupt enablings
	D[CONST 1] ROT[9.] ALU[DORQ] DEST[4] ;Set data ready bit
		SPEC[DEST-A-MEM] $

	D[MEM] ROT[33.] MASK[2] DEST[AR] ;Extract the mode
		COND[OBUS=0] JUMP[VCONO1] C550 $
	D[AR] ROT[1] DEST[Q] JUMP[VCONO2] NORM $ ; Double mode to Q

VCONO1:	D[CONST 1] ROT[1] DEST[Q] NORM $ ;Change mode 0 to 1 and double

VCONO2:	D[10] ALU[D+Q] SDISP C550 $ ;Set up constants by mode.
;
; Back from setup of constants.  Clear byte count.
;
VCSET2:	ALU[0] DEST[2] SPEC[DEST-A-MEM] JUMP[MAIN] $
;
; Read status/control bits

VCSTAT:
: VCDSP + 12 ; CONI dispatch

.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCSTAT] NORM $
: VCSTAT
]
	D[CONST VCDEV] DEST[DEV-ADR] SPEC[IOB-IN] PUSHJ[VCGST] NORM $
;		Fetch device status into AR.
	D[AR] DEST[MEMSTO] MEMST $
.REPEAT 1 - VCDEB [
: VCSTAT
]
VCONSZ:
: VCDSP + 14 ; CONSZ dispatch
.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCONSZ] NORM $
: VCONSZ
] ; VCDEB
	D[CONST VCDEV] DEST[DEV-ADR] SPEC[IOB-IN] PUSHJ[VCGST] NORM $
	D[IR] MASK[18.] DEST[Q] JUMP[CTYCZ] NORM $
.REPEAT 1 - VCDEB [
: VCONSZ
]
VCONSO:
: VCDSP + 16 ; CONSO dispatch
.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCONSO] NORM $
: VCONSO
] ; VCDEB
	D[CONST VCDEV] DEST[DEV-ADR] SPEC[IOB-IN] PUSHJ[VCGST] NORM $
	D[IR] MASK[18.] DEST[Q] JUMP[CTYCS] NORM $
.REPEAT 1 - VCDEB [
: VCONSO
]

VCGST:	MAPF[2] D[IOD] ROT[8.] MASK[8.] DEST[AR] CYLEN[IOB-IN] $
	D[AR] ROT[10.] DEST[Q] NORM $ ;Merge status with command.
	D[14] ALU[DORQ] DEST[AR] POPJ NORM $
;
VCOUNT:
.REPEAT VCDEB [		;BLKI - return byte count & MA.
:VCDSP + 2
	JUMP[.] $
	D[CONST VCDEV] DEST[DEV-ADR] JUMP[VCOUNT] NORM $
: VCOUNT
	D[12] ROT[18.] MASK[0] SPEC[LEFT] DEST[Q] NORM $
	D[11] MASK[18.] ALU[DORQ] DEST[MEMSTO] MEMST $
]
VCOUT:
: VCDSP + 6 ; DATAO dispatch

.REPEAT VCDEB [
	JUMP[.] NORM $
	JUMP[VCOUT] NORM $
: VCOUT
	FIXM1 $
	D[CONST VCDEV] DEST[DEV-ADR] NORM $
]
.REPEAT 1 - VCDEB [
	FIXM1 $
	D[CONST VCDEV] DEST[DEV-ADR] JUMP[VCOUT] NORM $
: VCOUT
]
;		Illegal op if BUSY is on.
	D[14] ROT[27.] MASK[1] COND[OBUS=0] JUMP[VCBAD] CYLEN[C550] $
	D[14] DEST[Q] NORM $
	D[CONST 1] ROT[9.] ALU[-D&Q] DEST[4] SPEC[DEST-A-MEM] NORM $ ; Clear DONE
	D[MASK 0] SPEC[LEFT] DEST[Q] NORM $ ;Pad byte count with ones in left.
	D[MEM] ROT[18.] MASK[18.] ALU[DORQ] DEST[2] SPEC[DEST-A-MEM] NORM $
	D[MEM] MASK[18.] DEST[1] SPEC[DEST-A-MEM] NORM $
;
VCGO:		;Q gets 0 for 7-bit bytes, 1 for 8-bit.
  .REPEAT NEWMAP [
	ALU[-1] DEST[MAP-DISABLE] NORM $
	  ;Disable mapping.
	   ]
  .REPEAT 1 - NEWMAP [
	DEST[CLR-DEV-FROM-INTR] SHORT $
	START-IN D[CONST 1] DEST[DEV-ADR] PUSHJ[MAPOFF] $
	MAPF[10] D[CONST VCDEV] DEST[DEV-ADR] C800 $
         ]
	D[14] ROT[32.] MASK[1] DEST[Q] NORM $
	D[CONST 7] ALU[D+Q] DEST[ROTR] NORM $ ;byte length to rotate and
	D[CONST 7] ALU[D+Q] DEST[MASKR] NORM $ ;mask registers.
	D[CONST 0] DEST[Q] NORM $ ;Q contains last-byte flag, initially 0.
	ALU[AC] DEST[6] SPEC[DEST-A-MEM] NORM $ ;Save AC and PC for scratching.
	D[PC] DEST[7] SPEC[DEST-A-MEM] NORM $
	D[11] DEST[PC] NORM $ ;PC gets current MA.
	D[12] DEST[AC] NORM $ ;AC gets (negative) byte count.
	D[13] ROT[32.] MASK[7] ALU[D+AC] DEST[AC] NORM $ ;Add burst count,
	ALU[AC] DEST[2] SPEC[DEST-A-MEM]
		COND[OBUS<0] JUMP[VCBRST] CYLEN[C550] $ ;check for overflow.
	D[13] ROT[32.] MASK[7] ALU[AC-D] DEST[AC] NORM $
;		Ov.  AC gets bytes remaining.
	D[CONST 0] DEST[2] SPEC[DEST-A-MEM] JUMP[VCLP] NORM $ ;Clear byte count.

VCBRST:	D[13] ROT[32.] MASK[7] ALU[0-D] DEST[AC] NORM $ ;No. ov.  AC_ burst ct.

VCLP:	PUSHJ[VCWRD] NORM $ ;Output a word.  If mode 1 or 2 and not last byte,
;		loop on these two instructions.
	MAPF[3] D[13] MASK[1] ALU[DORQ] COND[OBUS=0] JUMP[VCLP]
		LONG $
;		go to VCDONE if last byte.
	ALU[Q] COND[-OBUS=0] JUMP[VCDONE] CYLEN[C550] $
;
;		Mode 3.  Pull high 4 bits of the odd byte.
	D[MEM] ROT[4] MASK[4] DEST[HOLD] NORM $
;		Shift them to proper place in Q and fetch next word.
	SPEC[MA_PC] D[MEM] ROT[4] DEST[MA Q] NORM $
	FIXM1 $
	D[MEM] ROT[4] DEST[HOLD] NORM $ ;Low bits of odd byte to low end.
;		Check whether this is the last byte of the burst.
	ALU[AC+1] DEST[AC] COND[-OBUS<0] JUMP[VCLST] CYLEN[C550] $
;		No.  Put it out.
	SPEC[IOB-OUT] D[MEM] MASK[4] ALU[DORQ] DEST[IOD] NORM $
;		Set up byte count for second word.
	MAPF[3] D[13] ROT[35.] MASK[3] LLOAD CYLEN[IOB-OUT] $
;		Clear last-byte flag, put out second word.
	D[CONST 0] DEST[Q] PUSHJ[VCWRD1] NORM $
;		Loop for next double word if not end of burst.
	MAPF[3] ALU[Q] COND[OBUS=0] JUMP[VCLP] LONG $
;		End of burst.  Save updated MA and restore AC and PC.
VCDONE:	MAPF[3] D[PC] DEST[1] SPEC[DEST-A-MEM] NORM $
	SPEC[IOB-OUT] D[CONST 10] DEST[IOD] NORM $ ;Enable interrupt
	MAPF[4]D[17] DEST[PC] CYLEN[IOB-OUT] $
  .REPEAT NEWMAP [
	ALU[0] DEST[MAP-DISABLE] NORM $
	  ;Enable mapping.
         ]
  .REPEAT 1 - NEWMAP [
	D[IR] MASK[3] DEST[IOD] NORM $
	ALU[0] DEST[DEV-ADR] SPEC[IOB-OUT] NORM $
	MAPF[10] D[CONST VCDEV] DEST[DEV-ADR] C800 $
         ]
	D[16] DEST[CLR-DEV-FROM-INTR AC] JUMP[MAIN] NORM $
;		Odd byte is the last.  Put it out with the last-byte bit on.
VCLST:	D[CONST 1] ROT[34.] ALU[DORQ] DEST Q NORM $
	SPEC[IOB-OUT] D[MEM] MASK[4] ALU[DORQ] DEST[IOD] JUMP[VCDONE] NORM $

; Subroutine to output one word.  Instruction following call must have
; MAPF[3] CYLEN[IOB-OUT].
;
;		Entry for modes 1 and 2, and first word of a pair for
;		mode 3.  Set loop count to bytes/word.
VCWRD:	SPEC[MA_PC] DEST[MA] D[13] ROT[35.] MASK[3] LLOAD NORM $
	FIXM1 $
;		Second entry for other word in mode 3 (it's been fetched).
;		Add bytes/word to bytes remaining.
VCWRD1:	SPEC[PC+1] D[13] ROT[35.] MASK[3] ALU[D+AC] CARRY DEST[AC]
		COND[OBUS<0] JUMP[VCOUT1] CYLEN[C550] $
;		Overflow.  Change loop count to bytes remaining.
	D[13] ROT[35.] MASK[3] ALU[D-AC] DEST[AC] LLOAD NORM $
;		Set last-byte flag in Q.
	D[CONST 1] ROT[34.] DEST[Q] JUMP[VCOUT1] NORM $
;		This two-instruction loop puts out all but the last byte.
VCOLP:	D[MEM] MASK[R] DEST[IOD] PUSHJ[VCODLY] LONG $
VCOUT1:	MAPF[3] D[MEM] ROT[R] DEST[HOLD] LOOP[VCOLP] LONG $
;		Last byte here, with the flag bit set if end of burst.
	D[MEM] MASK[R] ALU[DORQ] DEST[IOD] LONG $
VCODLY:	NOP LONG $
	SPEC[IOB-OUT] POPJ LONG $

